﻿using System;
using System.Linq;
using System.Windows.Forms;
using System.IO;
using EmbeddedLinkUpdater.Code;
using EmbeddedLinkUpdater.Code.Events;
using System.Data;
using System.Threading;

namespace EmbeddedLinkUpdater
{
    public partial class FormFindAndReplace : Form
    {
        #region Properties

        private const string _logFile = "log.txt";
        private const string _replacementDataSetFile = "DataSetReplacements.xml";
        static ReaderWriterLock rwl = new ReaderWriterLock();

        private DocumentManager _documentManager;
        public DocumentManager DocumentManager
        {
            get { return _documentManager ?? (_documentManager = new DocumentManager()); }
        }

        private DataSet _dataSetResults;
        public DataSet DataSetResults
        {
            get { return _dataSetResults ?? (_dataSetResults = new DataSet()); }
        }

        #endregion

        protected delegate void dgAddToList(string Message);

        #region Form Events

        public FormFindAndReplace()
        {
            InitializeComponent();
        }

        private void FormFindAndReplace_Load(object sender, EventArgs e)
        {    
            SetCheckedListBoxCheckedState(true);

            DocumentManager.DocumentUpdateEvent += UpdateDocumentResult;
            DocumentManager.DocumentLinksEvent += UpdateDocumentLinksResult;

            LoadReplacements();
        }

        private void UpdateDocumentResult(object sender, DocumentUpdateDelegateArgs args)
        {
            LogAndUpdateResults(args);
        }

        private void UpdateDocumentLinksResult(object sender, DocumentLinksDelegateArgs args)
        {
            LogAndUpdateResults(args);
        }

        private void FormFindAndReplace_FormClosing(object sender, FormClosingEventArgs e)
        {
            SaveReplacements();
        }

        #endregion

        #region Button Events

        private void buttonUpdateLinks_Click(object sender, EventArgs e)
        {
            try
            {
                toolStripStatusLabel1.Text = String.Empty;

                if (!FormIsValid()) return;

                if (!Directory.Exists(textBoxDirectory.Text))
                    throw new ApplicationException("Directory entered does not exist or is not accessible.");

                if(dataSetReplacements.Tables[0].Rows.Count==0)
                    throw new ApplicationException("You must enter links to replace before you can update.");

                AddDateStampLineToLogFile("Started Link Update");

                SaveReplacements();

                var documentTypes = checkedListBoxDocTypes.Items.Cast<object>().Where((t, i) => checkedListBoxDocTypes.GetItemChecked(i)).Aggregate(string.Empty, (current, t) => current + (t + "|"));

                DocumentManager.UpdateLinks(textBoxDirectory.Text, documentTypes, dataSetReplacements);

                AddDateStampLineToLogFile("Finished Link Update");

                toolStripStatusLabel1.Text = String.Empty;
            }
            catch (ApplicationException ex)
            {
                Helper.ShowMessageDialog("Error", ex.Message);
            }
        }

        private void buttonAnalyzeLinks_Click(object sender, EventArgs e)
        {
            try
            {
                toolStripStatusLabel1.Text = String.Empty;

                if (!Directory.Exists(textBoxDirectory.Text))
                    throw new ApplicationException("Directory entered does not exist or is not accessible.");

                AddDateStampLineToLogFile("Started Link Analysis");

                var documentTypes = checkedListBoxDocTypes.Items.Cast<object>().Where((t, i) => checkedListBoxDocTypes.GetItemChecked(i)).Aggregate(string.Empty, (current, t) => current + (t + "|"));

                DocumentManager.AnalyzeLinks(textBoxDirectory.Text, documentTypes);

                AddDateStampLineToLogFile("Finished Link Analysis");

                toolStripStatusLabel1.Text = String.Empty;
            }
            catch (ApplicationException ex)
            {
                Helper.ShowMessageDialog("Error", ex.Message);
            }
        }

        private void buttonBrowse_Click(object sender, EventArgs e)
        {
            folderBrowserDialog1.SelectedPath = textBoxDirectory.Text;
            folderBrowserDialog1.ShowDialog();
            textBoxDirectory.Text = folderBrowserDialog1.SelectedPath;
        }

        private void buttonCancel_Click(object sender, EventArgs e)
        {
            LoadReplacements();
        }

        #endregion

        #region Methods

        private bool FormIsValid()
        {
            bool returnValue = true;

            errorProvider1.Clear();

            if(String.IsNullOrEmpty(textBoxDirectory.Text))
            {
                errorProvider1.SetError(textBoxDirectory, "Please select a directory");
                returnValue = false;
            }
            if (checkedListBoxDocTypes.CheckedIndices.Count == 0)
            {
                errorProvider1.SetError(checkedListBoxDocTypes, "Please select a document type");
                returnValue = false;
            }

            return returnValue;
        }

        private void SetCheckedListBoxCheckedState(bool checkState)
        {
            for (var i = 0; i < checkedListBoxDocTypes.Items.Count; i++)
                checkedListBoxDocTypes.SetItemChecked(i, checkState);
        }

        private void AddDateStampLineToLogFile(string timeStampLabel)
        {
            var logFile = File.AppendText(_logFile);
            logFile.WriteLine(String.Format("{0} {1}", timeStampLabel, DateTime.Now.ToString()));
            logFile.Close();
            logFile.Dispose();
        }

        private void LoadReplacements()
        {
            dataSetReplacements.Clear();
            if(File.Exists(_replacementDataSetFile))
                dataSetReplacements.ReadXml(_replacementDataSetFile);
        }

        private void SaveReplacements()
        {
            dataSetReplacements.WriteXml(_replacementDataSetFile);
        }

        public void AddToList(string Message)
        {
            lstResults.Items.Insert(0, Message);
            lblFileProcessedCount.Text = String.Format("{0} files processed", _documentManager.DocumentsProcessedCount);
        }

        protected void LogAndUpdateResults(object args)
        {
            try
            {
                rwl.AcquireWriterLock(1000);
                try
                {
                    var logFile = File.AppendText(_logFile);

                    if (args is DocumentLinksDelegateArgs)
                    {
                        var linkArgs = (DocumentLinksDelegateArgs)args;
                        if (linkArgs.Links != null)
                        {
                            foreach (var link in linkArgs.Links)
                            {
                                var status = File.Exists(link) ? "File exists." : "File does not exist.";

                                this.BeginInvoke(new dgAddToList(AddToList), new object[] { String.Format("Document:{0} Link:{1} status:{2}", linkArgs.DocumentName, link, status) });

                                logFile.WriteLine(String.Format("{0};{1};{2}", linkArgs.DocumentName, link, status));
                            }
                        }
                    }
                    else if (args is DocumentUpdateDelegateArgs)
                    {
                        var updateLinksArgs = (DocumentUpdateDelegateArgs)args;
                        lstResults.Items.Insert(0, String.Format("{0} ({1})", updateLinksArgs.FileName, updateLinksArgs.ResultMessage));
                    }

                    Application.DoEvents();

                    if (logFile == null) return;

                    logFile.Close();
                    logFile.Dispose();
                }
                finally
                {
                    // Ensure that the lock is released.
                    rwl.ReleaseWriterLock();
                }
            }
            catch (ApplicationException)
            {
            }
        }

        #endregion
    }
}
